﻿using Sustainalytics.Entities;
using Sustainalytics.ESG.Entities;
using Sustainalytics.ESGRatings.Computing.Entities;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

using System.Threading.Tasks;

namespace Sustainalytics.ESGRatings.BusinessLogic.Validators
{
    public interface IValidatable<out T>
    {
        bool Validate(IValidator<T> validator, out IEnumerable<string> brokenRules);
    }

    public interface IValidator<in T>
    {
        ICollection<string> BrokenRules(T entity);

        bool IsValid(T entity);
    }

    public interface IValidator<in T, in TK>
        where T : IEntity
    {
        ICollection<string> BrokenRules(T entity, TK entityAgainstValidate);

        Task<ICollection<string>> BrokenRulesAsync(T entity, TK entityAgainstValidate);

        bool IsValid(T entity, TK entityAgainstValidate);

        Task<bool> IsValidAsync(T entity, TK entityAgainstValidte);
    }

    public class CompanyWeightsValidator : IValidator<CompanyWeights, CompanyScores>
    {
        public ICollection<string> BrokenRules(CompanyWeights entity, CompanyScores entityAgainstValidate)
        {
            var brokenRulesList = new List<string>();
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            if (entityAgainstValidate == null)
            {
                brokenRulesList.Add(string.Format("Company {0} has no raw scores", entity.CompanyId));
                return brokenRulesList;
            }

            if (entity.Weights.IsNullOrEmpty())
            {
                brokenRulesList.Add(string.Format("Company {0} has no weights", entity.CompanyId));
                return brokenRulesList;
            }

            if (entityAgainstValidate.IndicatorsData.IsNullOrEmpty())
            {
                brokenRulesList.Add(string.Format("Company {0} has no raw scores", entity.CompanyId));
                return brokenRulesList;
            }

            if (entityAgainstValidate.IndicatorsData.Count() < entity.Weights.Count)
            {
                brokenRulesList.Add(
                    string.Format("Company {0} has missing scores in matrix {1}", entity.CompanyId, entity.MatrixId));
                return brokenRulesList;
            }

            var rawScoresCodes = new HashSet<string>(entityAgainstValidate.IndicatorsData.Select(x => x.Code));
            var weightsCodes = entity.Weights.Where(x => !rawScoresCodes.Contains(x.Key)).ToList();

            if (!weightsCodes.IsNullOrEmpty())
            {
                brokenRulesList.Add(
                    string.Format("Company {0} has missing scores in matrix {1}", entity.CompanyId, entity.MatrixId));
            }

            return brokenRulesList;
        }

        public Task<ICollection<string>> BrokenRulesAsync(CompanyWeights entity, CompanyScores entityAgainstValidate)
        {
            return Task.FromResult(BrokenRules(entity, entityAgainstValidate));
        }

        public bool IsValid(CompanyWeights entity, CompanyScores entityAgainstValidate)
        {
            return !this.BrokenRules(entity, entityAgainstValidate).Any();
        }

        public async Task<bool> IsValidAsync(CompanyWeights entity, CompanyScores entityAgainstValidte)
        {
            return (await BrokenRulesAsync(entity, entityAgainstValidte).ConfigureAwait(false)).Any();
        }
    }


    public class RawScoreValidator : IValidator<string>
    {
        public ICollection<string> BrokenRules(string entity)
        {
            var acceptedThemeCodesSet = new HashSet<string>() { "E", "S", "G" };
            var brokenRulesList = new List<string>();

            var indicatorNumber = entity;

            if (indicatorNumber.IsNullOrEmpty())
            {
                brokenRulesList.Add("Empty indicator number");

                return brokenRulesList;
            }

            if (!acceptedThemeCodesSet.Contains(indicatorNumber.Substring(0, 1).ToUpperInvariant()))
            {
                brokenRulesList.Add("Not in accepted theme list indicator");
            }

            return brokenRulesList;
        }

        public bool IsValid(string entity)
        {
            return !BrokenRules(entity).Any();
        }
    }

    public class WeightsValidator : IValidator<KeyValuePair<string, double>>
    {
        private readonly HashSet<string> _acceptedThemeCodesSet = new HashSet<string>() { { "E" }, { "S" }, { "G" }, };

        public ICollection<string> BrokenRules(KeyValuePair<string, double> entity)
        {
            var brokenRulesList = new List<string>();
            var themeSymbol = entity.Key.Substring(0, 1).ToUpperInvariant();
            if (!_acceptedThemeCodesSet.Contains(themeSymbol))
            {
                brokenRulesList.Add(string.Format("Indicator number not {0} not in accepted theme list", themeSymbol));
            }

            return brokenRulesList;
        }

        public bool IsValid(KeyValuePair<string, double> entity)
        {
            return !BrokenRules(entity).Any();
        }
    }
}